Skip to content

Replace cuda::atomic_ref with plain CUDA atomics#10001

Open
PointKernel wants to merge 4 commits into
NVIDIA:mainfrom
PointKernel:fix/cuco-plain-cuda-atomics-9983
Open

Replace cuda::atomic_ref with plain CUDA atomics#10001
PointKernel wants to merge 4 commits into
NVIDIA:mainfrom
PointKernel:fix/cuco-plain-cuda-atomics-9983

Conversation

@PointKernel

Copy link
Copy Markdown
Member

Description

Closes #9983

This PR replaces cuda::atomic_ref with plain CUDA atomics in the cuco open-addressing and HyperLogLog hot paths.

It adds internal helpers for the required relaxed compare-and-exchange, load/store, add, and max operations.

Checklist

  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@copy-pr-bot

copy-pr-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-project-automation github-project-automation Bot moved this to Todo in CCCL Jul 17, 2026
@cccl-authenticator-app cccl-authenticator-app Bot moved this from Todo to In Progress in CCCL Jul 17, 2026
@PointKernel

Copy link
Copy Markdown
Member Author

/ok to test 7608ed3

@PointKernel PointKernel added cuco cuCollections cudax Feature intended for the cudax experimental library labels Jul 17, 2026
@github-actions

This comment has been minimized.

@PointKernel

Copy link
Copy Markdown
Member Author

/ok to test 45c0caa

@github-actions

This comment has been minimized.

@PointKernel

Copy link
Copy Markdown
Member Author

/ok to test 07c9fbc

@github-actions

Copy link
Copy Markdown
Contributor

🥳 CI Workflow Results

🟩 Finished in 45m 21s: Pass: 100%/57 | Total: 13h 55m | Max: 45m 20s | Hits: 82%/46960

See results here.

@PointKernel
PointKernel marked this pull request as ready for review July 20, 2026 17:53
@PointKernel
PointKernel requested a review from a team as a code owner July 20, 2026 17:53
@cccl-authenticator-app cccl-authenticator-app Bot moved this from In Progress to In Review in CCCL Jul 20, 2026
@PointKernel

Copy link
Copy Markdown
Member Author

@wmaxey Requesting your review since you're the team's atomic expert.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added CUDA atomic utilities supporting compare-exchange, load, store, add, and maximum operations across supported data sizes and thread scopes.
    • Improved atomic updates used by HyperLogLog and open-addressing operations.
  • Bug Fixes

    • Preserved neighboring packed values during narrow atomic updates.
  • Tests

    • Added coverage for CUDA atomic operations, data integrity, and packed-value handling.

Walkthrough

Changes

This change adds CUCO-scoped CUDA atomic helpers for supported integral widths and replaces selected cuda::atomic_ref operations in HyperLogLog and open-addressing code. A CUDA test covers arithmetic, max, CAS, load, store, and packed-neighbor preservation.

CUCO plain CUDA atomics

Layer / File(s) Summary
Atomic utility implementation
cudax/include/cuda/experimental/__cuco/detail/utility/atomic.cuh
Adds scoped CAS, exchange, compare-exchange, load, store, fetch-add, and fetch-max helpers with type and width validation.
CUCO atomic integration
cudax/include/cuda/experimental/__cuco/detail/hyperloglog/..., cudax/include/cuda/experimental/__cuco/detail/open_addressing/...
Routes HyperLogLog register updates, insertion counters, key/payload CAS operations, stores, and payload loads through the new helpers.
Atomic correctness coverage
cudax/test/CMakeLists.txt, cudax/test/cuco/utility/test_atomic.cu
Registers a CUDA test covering multiple widths, scopes, atomic operations, CAS results, and adjacent guard values.

Assessment against linked issues

Objective Addressed Explanation
Replace CUCO cuda::atomic_ref operations with plain CUDA atomic helpers [9983]
Preserve all supported types and thread scopes, including explicit handling for 128-bit types and cluster scope [9983] The new utility supports 1-, 2-, 4-, and 8-byte types and thread, block, device, and system scopes; 128-bit and cluster scope handling are not present.
Add correctness coverage for packed-CAS paths [9983]
Add Release-build coverage below CUDA 13.1 and compare HLL/fixed-capacity-map performance [9983] The provided changes show neither dedicated older-toolkit Release coverage nor performance comparison code or results.

Suggested reviewers: srinivasyadav18


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
cudax/include/cuda/experimental/__cuco/detail/utility/atomic.cuh (1)

137-171: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

suggestion: Narrow-width __atomic_compare_exchange computes __aligned/__offset from the raw address without asserting _Tp's natural alignment. If a future 1/2-byte _Tp is only 1-byte aligned, __offset could put the packed window across a 4-byte boundary, corrupting adjacent memory. Consider a static/runtime assert on alignment, or document the natural-alignment requirement.

cudax/test/cuco/utility/test_atomic.cu (1)

38-64: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

suggestion: Coverage gap: __atomic_fetch_max is only exercised for a 4-byte type (line 44). No test covers the 8-byte signed path, which is exactly the combination flagged as needing verification in atomic.cuh's __atomic_fetch_max. See the root-cause comment there.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0c05c2d0-decd-47c8-bf15-79a6884bd28d

📥 Commits

Reviewing files that changed from the base of the PR and between 2ac8a0a and 07c9fbc.

📒 Files selected for processing (6)
  • cudax/include/cuda/experimental/__cuco/detail/hyperloglog/hyperloglog_impl.cuh
  • cudax/include/cuda/experimental/__cuco/detail/open_addressing/kernels.cuh
  • cudax/include/cuda/experimental/__cuco/detail/open_addressing/open_addressing_ref_impl.cuh
  • cudax/include/cuda/experimental/__cuco/detail/utility/atomic.cuh
  • cudax/test/CMakeLists.txt
  • cudax/test/cuco/utility/test_atomic.cu

Comment on lines +746 to +765
const auto key_cas_success = ::cuda::experimental::cuco::detail::__atomic_compare_exchange<_Scope>(
&__address->first, __expected_key, __key_type{__desired.first});
auto payload_cas_success = ::cuda::experimental::cuco::detail::__atomic_compare_exchange<_Scope>(
&__address->second, __expected_payload, mapped_type{__desired.second});

// if __key success
if (key_cas_success)
{
while (!payload_cas_success)
{
payload_cas_success = payload_ref.compare_exchange_strong(
__expected_payload = empty_value_sentinel(), __desired.second, ::cuda::memory_order_relaxed);
__expected_payload = empty_value_sentinel();
payload_cas_success = ::cuda::experimental::cuco::detail::__atomic_compare_exchange<_Scope>(
&__address->second, __expected_payload, mapped_type{__desired.second});
}
return __insert_result::__success;
}
else if (payload_cas_success)
{
// This is insert-specific, cannot for `erase` operations
payload_ref.store(empty_value_sentinel(), ::cuda::memory_order_relaxed);
::cuda::experimental::cuco::detail::__atomic_store<_Scope>(&__address->second, empty_value_sentinel());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

important: Logic transcription from the prior atomic_ref version is correct. The __atomic_store call at line 765 depends on mapped_type being 4/8 bytes per atomic.cuh's current constraints — see the root-cause comment on atomic.cuh's __atomic_load/__atomic_store for the narrow-payload risk this call inherits.

Comment on lines 794 to +801
auto __expected_key = __expected.first;
const auto success =
key_ref.compare_exchange_strong(__expected_key, __key_type{__desired.first}, ::cuda::memory_order_relaxed);
const auto success = ::cuda::experimental::cuco::detail::__atomic_compare_exchange<_Scope>(
&__address->first, __expected_key, __key_type{__desired.first});

// if __key success
if (success)
{
::cuda::atomic_ref<mapped_type, _Scope> payload_ref(__address->second);
payload_ref.store(__desired.second, ::cuda::memory_order_relaxed);
::cuda::experimental::cuco::detail::__atomic_store<_Scope>(&__address->second, mapped_type{__desired.second});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

important: Same narrow-payload dependency as back_to_back_cas: the __atomic_store at line 801 requires a 4/8-byte mapped_type. See the root-cause comment on atomic.cuh.

Comment on lines 889 to +893
_Value __current;
// TODO exponential backoff strategy
do
{
__current = __ref.load(::cuda::std::memory_order_relaxed);
__current = ::cuda::experimental::cuco::detail::__atomic_load<_Scope>(&__slot);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

important: __atomic_load<_Scope> requires a 4/8-byte _Value; __wait_for_payload is generic over any slot type, so this inherits the same narrow-width gap flagged on atomic.cuh.

Comment on lines +186 to +209
template <::cuda::thread_scope _Scope, class _Tp>
[[nodiscard]] _CCCL_DEVICE_API _Tp __atomic_load(const _Tp* __address) noexcept
{
::cuda::experimental::cuco::detail::__validate_atomic_type<_Tp>();
static_assert(sizeof(_Tp) == 4 || sizeof(_Tp) == 8, "cuCO atomic load requires a 4 or 8 byte type");

using __word_type = __atomic_word_t<_Tp>;
auto* const __word_address = reinterpret_cast<__word_type*>(const_cast<_Tp*>(__address));
constexpr __word_type __zero = 0;
const auto __old = ::cuda::experimental::cuco::detail::__atomic_cas_word<_Scope>(__word_address, __zero, __zero);
return ::cuda::std::bit_cast<_Tp>(__old);
}

template <::cuda::thread_scope _Scope, class _Tp>
_CCCL_DEVICE_API void __atomic_store(_Tp* __address, _Tp __value) noexcept
{
::cuda::experimental::cuco::detail::__validate_atomic_type<_Tp>();
static_assert(sizeof(_Tp) == 4 || sizeof(_Tp) == 8, "cuCO atomic store requires a 4 or 8 byte type");

using __word_type = __atomic_word_t<_Tp>;
auto* const __word_address = reinterpret_cast<__word_type*>(__address);
const __word_type __desired = ::cuda::std::bit_cast<__word_type>(__value);
(void) ::cuda::experimental::cuco::detail::__atomic_exchange_word<_Scope>(__word_address, __desired);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

important: __atomic_load/__atomic_store only support 4/8-byte types (static_assert(sizeof(_Tp) == 4 || sizeof(_Tp) == 8, ...)), with no narrow (<=2 byte) fallback, unlike __atomic_compare_exchange which explicitly implements a packed-CAS path for small types. The previous cuda::atomic_ref<T, Scope> supported arbitrary trivially-copyable sizes. open_addressing_ref_impl.cuh's __wait_for_payload, back_to_back_cas, and cas_dependent_write call __atomic_load/__atomic_store generically on payload types that are not guaranteed to be >=4 bytes (e.g. a key/payload pair combined size >8 bytes with a 1-2 byte payload). Such instantiations will fail to compile where the old atomic_ref-based code compiled fine.

🛠️ Suggested direction

Mirror the packed-CAS technique already implemented in __atomic_compare_exchange to add a narrow-width load (peek via CAS(0,0) on the aligned word, masked/shifted) and narrow-width store (CAS loop overwriting only the target bits).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuco cuCollections cudax Feature intended for the cudax experimental library

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

[CUCO][FEA]: Use plain CUDA atomics instead of cuda::atomic_ref

1 participant